import { Log } from "../../../../../Core/Module/Log/Log";
import { ATimer } from "../../../../../Core/Module/Timer/ATimer";
import { ObjectSystemAttribute, FriendOf, ExpandOf, InvokeAttribute } from "../../../../../Decorator/Decorator";
import { ActorLocationSenderComponent } from "../../../../Model/Server/Module/ActorLocation/ActorLocationSenderComponent";
import { TimerInvokeType } from "../../../../Model/Share/TimerInvokeType";
import { AwakeSystem } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { ActorLocationSender } from "../../../../Model/Server/Module/ActorLocation/ActorLocationSender";
import { Exception, RpcException } from "../../../../../Core/Object/Exception";
import { IActorRequest, IActorResponse } from "../../../../Model/Share/Module/Actor/IActorMessage";
import { ActorMessageSenderComponent } from "../../../../Model/Server/Module/Actor/ActorMessageSenderComponent";
import { CoroutineLockComponent } from "../../../../../Core/Module/CoroutineLock/CoroutineLockComponent";
import { CoroutineLockType } from "../../../../../Core/Module/CoroutineLock/CoroutineLockType";
import { ErrorCore } from "../../../../../Core/Module/Network/ErrorCore";
import { ActorHelper } from "../Actor/ActorHelper";
import { TimeHelper } from "../../../../../Core/Module/Time/TimeHelper";
import { LocationProxyComponent } from "../../../../Model/Server/Module/ActorLocation/LocationProxyComponent";
import { TimerComponent } from "../../../../../Core/Module/Timer/TimerComponent";
import { ListComponent } from "../../../../../Support/Share/Lib/ListComponent";

@InvokeAttribute(TimerInvokeType.ActorLocationSenderChecker)
export class ActorLocationSenderChecker extends ATimer<ActorLocationSenderComponent> {

    protected override Run(self: ActorLocationSenderComponent): void {
        try {
            self.Check();
        } catch (e) {
            Log.Error(`move timer error: ${self.Id}\n${e}`);
        }
    }

}

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "../../../../Model/Server/Module/ActorLocation/ActorLocationSenderComponent" {

    interface ActorLocationSenderComponent {
        Check(): void;
        GetOrCreate(id: number): ActorLocationSender;
        Remove(id: number): void;
        Send(entityId: number, message: IActorRequest): void;
        Call(entityId: number, iActorRequest: IActorRequest): Promise<IActorResponse>;
        CallInner(actorLocationSender: ActorLocationSender, rpcId: number, iActorRequest: IActorRequest): Promise<IActorResponse>;
    }

}

@ExpandOf(ActorLocationSenderComponent)
export class ActorLocationSenderComponentSystem {

    private static Check(self: ActorLocationSenderComponent): void {
        let list = ListComponent.Create<number>();
        {
            let timeNow = TimeHelper.ServerNow();
            for (const keyStr in self.Children) {
                const key = parseInt(keyStr);
                const value = self.Children.TryGetValue(key);
                const actorLocationMessageSender = <ActorLocationSender>value;

                if (timeNow > actorLocationMessageSender.LastSendOrRecvTime + ActorLocationSenderComponent.TIMEOUT_TIME) {
                    list.Add(key);
                }
            }
            for (const idStr in list) {
                const id = parseInt(idStr);
                self.Remove(id);
            }
        }
        list.Dispose();
        list = null;
    }

    private static GetOrCreate(self: ActorLocationSenderComponent, id: number): ActorLocationSender {
        if (id == 0) {
            throw new Exception(`actor id is 0`);
        }
        let actorLocationSender = self.Children.TryGetValue(id);
        if (actorLocationSender) {
            return <ActorLocationSender>actorLocationSender;
        }
        actorLocationSender = self.AddChildWithId<ActorLocationSender>(ActorLocationSender, id);
        return <ActorLocationSender>actorLocationSender;
    }

    private static Remove(self: ActorLocationSenderComponent, id: number): void {
        let actorMessageSender = self.Children.TryGetValue(id);
        if (!actorMessageSender) {
            return;
        }
        actorMessageSender.Dispose();
    }

    private static Send(self: ActorLocationSenderComponent, entityId: number, message: IActorRequest): void {
        self.Call(entityId, message).then();
    }

    private static async Call(self: ActorLocationSenderComponent, entityId: number, iActorRequest: IActorRequest): Promise<IActorResponse> {
        let actorLocationSender = self.GetOrCreate(entityId);

        // 先序列化好
        let rpcId = ActorMessageSenderComponent.Instance.GetRpcId();
        iActorRequest.RpcId = rpcId;

        let actorLocationSenderInstanceId = actorLocationSender.InstanceId;
        let coroutineLock = await CoroutineLockComponent.Instance.Wait(CoroutineLockType.ActorLocationSender, entityId);
        if (actorLocationSender.InstanceId != actorLocationSenderInstanceId) {
            throw new RpcException(ErrorCore.ERR_ActorTimeout, `${iActorRequest}`);
        }

        // 队列中没处理的消息返回跟上个消息一样的报错
        if (actorLocationSender.Error == ErrorCore.ERR_NotFoundActor) {
            return ActorHelper.CreateResponse(iActorRequest, actorLocationSender.Error);
        }

        try {
            return await self.CallInner(actorLocationSender, rpcId, iActorRequest);
        } catch (e) {
            self.Remove(actorLocationSender.Id);
            throw new Exception(`${iActorRequest}`);
        } finally {
            coroutineLock.Dispose();
        }
    }

    private static async CallInner(self: ActorLocationSenderComponent, actorLocationSender: ActorLocationSender, rpcId: number, iActorRequest: IActorRequest): Promise<IActorResponse> {
        let failTimes = 0;
        let instanceId = actorLocationSender.InstanceId;
        actorLocationSender.LastSendOrRecvTime = TimeHelper.ServerNow();

        while (true) {
            if (actorLocationSender.ActorId == 0) {
                actorLocationSender.ActorId = await LocationProxyComponent.Instance.Get(actorLocationSender.Id);
                if (actorLocationSender.InstanceId != instanceId) {
                    throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout2, `${iActorRequest}`);
                }
            }

            if (actorLocationSender.ActorId == 0) {
                actorLocationSender.Error = ErrorCore.ERR_NotFoundActor;
                return ActorHelper.CreateResponse(iActorRequest, ErrorCore.ERR_NotFoundActor);
            }
            let response = await ActorMessageSenderComponent.Instance.CallRpc(actorLocationSender.ActorId, rpcId, iActorRequest, false);
            if (actorLocationSender.InstanceId != instanceId) {
                throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout3, `${iActorRequest}`);
            }

            switch (response.Error) {
                case ErrorCore.ERR_NotFoundActor:
                    {
                        // 如果没找到Actor,重试
                        ++failTimes;
                        if (failTimes > 20) {
                            Log.Debug(`actor send message fail, actorid: ${actorLocationSender.Id}`);
                            actorLocationSender.Error = ErrorCore.ERR_NotFoundActor;
                            // 这里不能删除actor，要让后面等待发送的消息也返回ERR_NotFoundActor，直到超时删除
                            return response;
                        }
                        // 等待0.5s再发送
                        await TimerComponent.Instance.WaitAsync(500);
                        if (actorLocationSender.InstanceId != instanceId) {
                            throw new RpcException(ErrorCore.ERR_ActorLocationSenderTimeout4, `${iActorRequest}`);
                        }

                        actorLocationSender.ActorId = 0;
                        continue;
                    }
                case ErrorCore.ERR_ActorTimeout:
                    {
                        throw new RpcException(response.Error, `${iActorRequest}`);
                    }
            }

            if (ErrorCore.IsRpcNeedThrowException(response.Error)) {
                throw new RpcException(response.Error, `Message: ${response.Message} Request: ${iActorRequest}`);
            }

            return response;
        }
    }

}

@FriendOf(ActorLocationSenderComponent)
@ObjectSystemAttribute()
export class ActorLocationSenderComponentAwakeSystem extends AwakeSystem<ActorLocationSenderComponent> {

    Awake(self: ActorLocationSenderComponent): void {
        ActorLocationSenderComponent.Instance = self;

        // 每10s扫描一次过期的actorproxy进行回收,过期时间是2分钟
        // 可能由于bug或者进程挂掉，导致ActorLocationSender发送的消息没有确认，结果无法自动删除，每一分钟清理一次这种ActorLocationSender
        self.CheckTimer = TimerComponent.Instance.NewRepeatedTimer(10 * 1000, TimerInvokeType.ActorLocationSenderChecker, self);
    }

}

@FriendOf(ActorLocationSenderComponent)
@ObjectSystemAttribute()
export class ActorLocationSenderComponentDestroySystem extends DestroySystem<ActorLocationSenderComponent> {

    Destroy(self: ActorLocationSenderComponent): void {
        ActorLocationSenderComponent.Instance = null;
        TimerComponent.Instance?.Remove(self.CheckTimer);
    }

}